#include "StdAfx.h"
#include "Tile.h"
#include "TypeInfo_impl.h"

STRUCT_INFO_BEGIN(MNM::Tile::Triangle)
STRUCT_VAR_INFO(vertex, TYPE_ARRAY(3,TYPE_INFO(Index)))
STRUCT_BITFIELD_INFO(linkCount, TYPE_INFO(uint16), 4)
STRUCT_BITFIELD_INFO(firstLink, TYPE_INFO(uint16), 12)
STRUCT_INFO_END(MNM::Tile::Triangle)

STRUCT_INFO_BEGIN(MNM::Tile::BVNode)
STRUCT_BITFIELD_INFO(leaf, TYPE_INFO(uint16), 1)
STRUCT_BITFIELD_INFO(offset, TYPE_INFO(uint16), 15)
STRUCT_VAR_INFO(aabb, TYPE_INFO(AABB))
STRUCT_INFO_END(MNM::Tile::BVNode)

STRUCT_INFO_BEGIN(MNM::Tile::Link)
STRUCT_BITFIELD_INFO(side, TYPE_INFO(uint32), 4)
STRUCT_BITFIELD_INFO(edge, TYPE_INFO(uint32), 2)
STRUCT_BITFIELD_INFO(triangle, TYPE_INFO(uint32), 10)
STRUCT_INFO_END(MNM::Tile::Link)

#include "Navigation/MNM/MNM_Type_info.h"


namespace MNM
{
	Tile::Tile()
		: triangles(0)
		, vertices(0)
		, nodes(0)
		, links(0)
		, triangleCount(0)
		, vertexCount(0)
		, nodeCount(0)
		, linkCount(0)
		, hashValue(0)
	{

	}

	void Tile::CopyTriangles(Triangle* _triangles, uint16 count)
	{

#if MNM_USE_EXPORT_INFORMATION
		InitConnectivity(triangleCount, count);
#endif

		if (triangleCount != count)
		{
			delete[] triangles;
			triangles = 0;

			triangleCount = count;

			if (count)
				triangles = new Triangle[count];
		}

		if (count)
			memcpy(triangles, _triangles, sizeof(Triangle) * count);
	}

	void Tile::CopyVertices(Vertex* _vertices, uint16 count)
	{
		if (vertexCount != count)
		{
			delete[] vertices;
			vertices = 0;

			vertexCount = count;

			if (count)
				vertices = new Vertex[count];
		}

		if (count)
			memcpy(vertices, _vertices, sizeof(Vertex) * count);
	}

	void Tile::CopyNodes(BVNode* _nodes, uint16 count)
	{
		if (nodeCount != count)
		{
			delete[] nodes;
			nodes = 0;

			nodeCount = count;

			if (count)
				nodes = new BVNode[count];
		}

		if (count)
			memcpy(nodes, _nodes, sizeof(BVNode) * count);
	}

	void Tile::CopyLinks(Link* _links, uint16 count)
	{
		if (linkCount != count)
		{
			delete[] links;
			links = 0;

			linkCount = count;

			if (count)
				links = new Link[count];
		}

		if (count)
			memcpy(links, _links, sizeof(Link) * count);

	}

	void Tile::AddOffMeshLink(const TriangleID triangleID, const uint16 offMeshIndex)
	{
		uint16 triangleIdx = ComputeTriangleIndex(triangleID);
		if ( triangleIdx < triangleCount )
		{
			//Figure out if this triangle has already off-mesh connections
			//Off-mesh link is always the first if exists
			Triangle& triangle = triangles[triangleIdx];

			const size_t MaxLinkCount = 1024 * 6;
			Tile::Link tempLinks[MaxLinkCount];

			if (!links || (links[triangle.firstLink].side != Link::OffMesh))
			{
				//Add off-mesh link for triangle
				{
					if (triangle.firstLink)
					{
						assert(links);
						memcpy(tempLinks, links, sizeof(Link) * triangle.firstLink);
					}

					tempLinks[triangle.firstLink].side = Link::OffMesh;
					tempLinks[triangle.firstLink].triangle = offMeshIndex;	

					//Note this is not used
					tempLinks[triangle.firstLink].edge = 0;	

					const int countDiff = linkCount - triangle.firstLink;
					if (countDiff)
					{
						assert(links);
						memcpy(&tempLinks[triangle.firstLink + 1], &links[triangle.firstLink], sizeof(Link) * countDiff);
					}
				}

				CopyLinks(tempLinks, linkCount + 1);

				//Re-arrange link indices for triangles
				triangle.linkCount++;

				for (uint16 tIdx = (triangleIdx + 1); tIdx < triangleCount; ++tIdx)
				{
					triangles[tIdx].firstLink += triangles[tIdx].linkCount ? 1 : 0;
				}
			}
		}
	}

	void Tile::UpdateOffMeshLink(const TriangleID triangleID, const uint16 offMeshIndex)
	{
		uint16 triangleIndex = ComputeTriangleIndex(triangleID);
		assert((triangleIndex >= 0) && (triangleIndex < triangleCount));
		if ( triangleIndex < triangleCount )
		{
			//First link is always off-mesh if exists
			Link& link = links[triangles[triangleIndex].firstLink];
			assert(link.side == Link::OffMesh);
			if (link.side == Link::OffMesh)
			{
				link.triangle = offMeshIndex;
			}
		}
	}

	void Tile::RemoveOffMeshLink(const TriangleID triangleID)
	{
		// Find link to be removed
		uint16 linkToRemoveIdx = 0xFFFF;
		uint16 boundTriangleIdx = ComputeTriangleIndex(triangleID); 
		if ( boundTriangleIdx < triangleCount )
		{
			if((triangles[boundTriangleIdx].linkCount > 0) && links[triangles[boundTriangleIdx].firstLink].side == Link::OffMesh)
				linkToRemoveIdx = triangles[boundTriangleIdx].firstLink;
		}

		if (linkToRemoveIdx != 0xFFFF)
		{
			assert(linkCount > 1);

			const size_t MaxLinkCount = 1024 * 6;
			Tile::Link tempLinks[MaxLinkCount];

			if (linkToRemoveIdx)
				memcpy(tempLinks, links, sizeof(Link) * linkToRemoveIdx);

			const int diffCount = linkCount - (linkToRemoveIdx+1);
			if (diffCount > 0)
			{
				memcpy(&tempLinks[linkToRemoveIdx], &links[linkToRemoveIdx+1], sizeof(Link) * diffCount);
			}

			CopyLinks(tempLinks, linkCount - 1);

			//Re-arrange link indices for triangles
			triangles[boundTriangleIdx].linkCount--;

			for (uint16 tIdx = (boundTriangleIdx + 1); tIdx < triangleCount; ++tIdx)
			{
				triangles[tIdx].firstLink -= triangles[tIdx].linkCount ? 1 : 0;
			}
		}
	}

	void Tile::Swap(Tile& other)
	{
		std::swap(triangles, other.triangles);
		std::swap(vertices, other.vertices);
		std::swap(nodes, other.nodes);
		std::swap(links, other.links);

#if MNM_USE_EXPORT_INFORMATION
		InitConnectivity(triangleCount, other.triangleCount);
#endif

		std::swap(triangleCount, other.triangleCount);
		std::swap(vertexCount, other.vertexCount);
		std::swap(nodeCount, other.nodeCount);
		std::swap(linkCount, other.linkCount);
		std::swap(hashValue, other.hashValue);

	}

	void Tile::Destroy()
	{
		delete[] triangles;
		triangles = 0;
		
		delete[] vertices;
		vertices = 0;
		
		delete[] nodes;
		nodes = 0;
		
		delete[] links;
		links = 0;

#if MNM_USE_EXPORT_INFORMATION
		SAFE_DELETE_ARRAY(connectivity.trianglesAccessible);
		connectivity.tileAccessible = false;
#endif

		triangleCount = 0;
		vertexCount = 0;
		nodeCount = 0;
		linkCount = 0;
		hashValue = 0;
	}

	void Tile::Draw(size_t drawFlags, vector3_t origin) const
	{
		IRenderAuxGeom* renderAuxGeom = gEnv->pRenderer->GetIRenderAuxGeom();

		const ColorB triangleColorConnected(Col_SlateBlue, 0.65f);
		const ColorB triangleColorDisconnected(Col_Red, 0.65f);
		const ColorB boundaryColor(Col_Black);

		const Vec3 offset = origin.GetVec3() + Vec3(0.0f, 0.0f, 0.05f);
		const Vec3 loffset(offset + Vec3(0.0f, 0.0f, 0.0005f));

		SAuxGeomRenderFlags oldFlags = renderAuxGeom->GetRenderFlags();
		SAuxGeomRenderFlags renderFlags(oldFlags);

		renderFlags.SetAlphaBlendMode(e_AlphaBlended);
		renderFlags.SetDepthWriteFlag(e_DepthWriteOff);

		renderAuxGeom->SetRenderFlags(renderFlags);

		if (drawFlags & DrawTriangles)
		{
			for (size_t i = 0; i < triangleCount; ++i)
			{
				const Triangle& triangle = triangles[i];

				const Vec3 v0 = vertices[triangle.vertex[0]].GetVec3() + offset;
				const Vec3 v1 = vertices[triangle.vertex[1]].GetVec3() + offset;
				const Vec3 v2 = vertices[triangle.vertex[2]].GetVec3() + offset;

#if MNM_USE_EXPORT_INFORMATION
				const ColorB& triangleColor = ((drawFlags & DrawAccessibility) && (connectivity.trianglesAccessible != NULL) && !connectivity.trianglesAccessible[i]) ? triangleColorDisconnected : triangleColorConnected;
#else
				const ColorB& triangleColor = triangleColorConnected;
#endif

				renderAuxGeom->DrawTriangle(v0, triangleColor, v1, triangleColor, v2, triangleColor);
			}
		}

		renderAuxGeom->SetRenderFlags(oldFlags);

		for (size_t i = 0; i < triangleCount; ++i)
		{
			const Triangle& triangle = triangles[i];
			size_t linkedEdges = 0;

			for (size_t l = 0; l < triangle.linkCount; ++l)
			{
				const Link& link = links[triangle.firstLink + l];
				const size_t edge = link.edge;
				linkedEdges |= static_cast<size_t>(1 << edge);

				const uint16 vi0 = link.edge;
				const uint16 vi1 = (link.edge + 1) % 3;

				assert(vi0 < 3);
				assert(vi1 < 3);

				const Vec3 v0 = vertices[triangle.vertex[vi0]].GetVec3() + loffset;
				const Vec3 v1 = vertices[triangle.vertex[vi1]].GetVec3() + loffset;

				if (link.side == Link::OffMesh)
				{
					if (drawFlags & DrawOffMeshLinks)
					{
						const Vec3 a = vertices[triangle.vertex[0]].GetVec3() + offset;
						const Vec3 b = vertices[triangle.vertex[1]].GetVec3() + offset;
						const Vec3 c = vertices[triangle.vertex[2]].GetVec3() + offset;

						renderAuxGeom->DrawLine(a, Col_Red, b, Col_Red, 8.0f);
						renderAuxGeom->DrawLine(b, Col_Red, c, Col_Red, 8.0f);
						renderAuxGeom->DrawLine(c, Col_Red, a, Col_Red, 8.0f);
					}
				}
				else if (link.side != Link::Internal)
				{
					if (drawFlags & DrawExternalLinks)
					{
						// TODO: compute clipped edge
						renderAuxGeom->DrawLine(v0, Col_Green, v1, Col_ForestGreen);
					}
				}
				else
				{
					if (drawFlags & DrawInternalLinks)
						renderAuxGeom->DrawLine(v0, Col_White, v1, Col_White);
				}
			}

			if (drawFlags & DrawMeshBoundaries)
			{
				for (size_t e = 0; e < 3; ++e)
				{
					if ((linkedEdges & static_cast<size_t>(1 << e)) == 0)
					{
						const Vec3 a = vertices[triangle.vertex[e]].GetVec3() + loffset;
						const Vec3 b = vertices[triangle.vertex[(e + 1) % 3]].GetVec3() + loffset;

						renderAuxGeom->DrawLine(a, Col_Black, b, Col_Black, 8.0f);
					}
				}
			}
		}
	}

//////////////////////////////////////////////////////////////////////////

#if MNM_USE_EXPORT_INFORMATION

	bool Tile::ConsiderExportInformation() const
	{
		return gEnv->IsEditor();
	}

	void Tile::InitConnectivity( uint16 oldTriangleCount, uint16 newTriangleCount )
	{
		if (ConsiderExportInformation())
		{
			// By default all is accessible
			connectivity.tileAccessible = 1;

			if (oldTriangleCount != newTriangleCount)
			{
				SAFE_DELETE_ARRAY(connectivity.trianglesAccessible);

				if (newTriangleCount)
					connectivity.trianglesAccessible = new uint8[newTriangleCount];
			}

			if (newTriangleCount)
				memset(connectivity.trianglesAccessible, 1, sizeof(uint8) * newTriangleCount);

			connectivity.triangleCount = newTriangleCount;
		}
	}

	void Tile::ResetConnectivity(uint8 accessible)
	{
		if (ConsiderExportInformation())
		{
			assert(connectivity.triangleCount == triangleCount);

			connectivity.tileAccessible = accessible;

			if (connectivity.trianglesAccessible != NULL)
			{
				memset(connectivity.trianglesAccessible, accessible, sizeof(uint8) * connectivity.triangleCount);
			}
		}
	}

#endif
}